/*	$OpenBSD: rf_pq.c,v 1.5 2000/01/11 18:02:22 peter Exp $	*/
/*	$NetBSD: rf_pq.c,v 1.7 2000/01/07 03:41:02 oster Exp $	*/

/*
 * Copyright (c) 1995 Carnegie-Mellon University.
 * All rights reserved.
 *
 * Author: Daniel Stodolsky
 *
 * Permission to use, copy, modify and distribute this software and
 * its documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 *
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * Carnegie Mellon requests users of this software to return to
 *
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 * any improvements or extensions that they make and grant Carnegie the
 * rights to redistribute these changes.
 */

/*
 * Code for RAID level 6 (P + Q) disk array architecture.
 */

#include "rf_archs.h"
#include "rf_types.h"
#include "rf_raid.h"
#include "rf_dag.h"
#include "rf_dagffrd.h"
#include "rf_dagffwr.h"
#include "rf_dagdegrd.h"
#include "rf_dagdegwr.h"
#include "rf_dagutils.h"
#include "rf_dagfuncs.h"
#include "rf_etimer.h"
#include "rf_pqdeg.h"
#include "rf_general.h"
#include "rf_map.h"
#include "rf_pq.h"

RF_RedFuncs_t rf_pFuncs = {
	rf_RegularONPFunc, "Regular Old-New P",
	rf_SimpleONPFunc, "Simple Old-New P"
};
RF_RedFuncs_t rf_pRecoveryFuncs = {
	rf_RecoveryPFunc, "Recovery P Func",
	rf_RecoveryPFunc, "Recovery P Func"
};

int
rf_RegularONPFunc(RF_DagNode_t *node)
{
	return (rf_RegularXorFunc(node));
}


/*
 * Same as simpleONQ func, but the coefficient is always 1.
 */

int
rf_SimpleONPFunc(RF_DagNode_t *node)
{
	return (rf_SimpleXorFunc(node));
}

int
rf_RecoveryPFunc(RF_DagNode_t *node)
{
	return (rf_RecoveryXorFunc(node));
}

int
rf_RegularPFunc(RF_DagNode_t *node)
{
	return (rf_RegularXorFunc(node));
}


#if	(RF_INCLUDE_DECL_PQ > 0) || (RF_INCLUDE_RAID6 > 0)

void rf_QDelta(char *dest, char *obuf, char *nbuf, unsigned length,
	unsigned char coeff);
void rf_InvertQ(unsigned long *qbuf, unsigned long *abuf, unsigned length,
	unsigned coeff);

RF_RedFuncs_t rf_qFuncs = {
	rf_RegularONQFunc, "Regular Old-New Q",
	rf_SimpleONQFunc, "Simple Old-New Q"
};
RF_RedFuncs_t rf_qRecoveryFuncs = {
	rf_RecoveryQFunc, "Recovery Q Func",
	rf_RecoveryQFunc, "Recovery Q Func"
};
RF_RedFuncs_t rf_pqRecoveryFuncs = {
	rf_RecoveryPQFunc, "Recovery PQ Func",
	rf_RecoveryPQFunc, "Recovery PQ Func"
};

void
rf_PQDagSelect(RF_Raid_t *raidPtr, RF_IoType_t type,
    RF_AccessStripeMap_t *asmap, RF_VoidFuncPtr *createFunc)
{
	RF_RaidLayout_t *layoutPtr = &(raidPtr->Layout);
	unsigned ndfail = asmap->numDataFailed;
	unsigned npfail = asmap->numParityFailed;
	unsigned ntfail = npfail + ndfail;

	RF_ASSERT(RF_IO_IS_R_OR_W(type));
	if (ntfail > 2) {
		RF_ERRORMSG("more than two disks failed in a single group !"
		            "  Aborting I/O operation.\n");
		 /* *infoFunc = */ *createFunc = NULL;
		return;
	}
	/* Ok, we can do this I/O. */
	if (type == RF_IO_TYPE_READ) {
		switch (ndfail) {
		case 0:
			/* Fault free read. */
			*createFunc = (RF_VoidFuncPtr)
			    rf_CreateFaultFreeReadDAG;	/* Same as raid 5. */
			break;
		case 1:
			/* Lost a single data unit. */
			/*
			 * Two cases:
			 * (1) Parity is not lost. Do a normal raid 5
			 *     reconstruct read.
			 * (2) Parity is lost. Do a reconstruct read using "q".
			 */
			if (ntfail == 2) {	/* Also lost redundancy. */
				if (asmap->failedPDAs[1]->type ==
				    RF_PDA_TYPE_PARITY)
					*createFunc = (RF_VoidFuncPtr)
					    rf_PQ_110_CreateReadDAG;
				else
					*createFunc = (RF_VoidFuncPtr)
					    rf_PQ_101_CreateReadDAG;
			} else {
				/*
				 * P and Q are ok. But is there a failure in
				 * some unaccessed data unit ?
				 */
				if (rf_NumFailedDataUnitsInStripe(raidPtr,
				    asmap) == 2)
					*createFunc = (RF_VoidFuncPtr)
					    rf_PQ_200_CreateReadDAG;
				else
					*createFunc = (RF_VoidFuncPtr)
					    rf_PQ_100_CreateReadDAG;
			}
			break;
		case 2:
			/* Lost two data units. */
			/* *infoFunc = rf_PQOneTwo; */
			*createFunc = (RF_VoidFuncPtr) rf_PQ_200_CreateReadDAG;
			break;
		}
		return;
	}
	/* A write. */
	switch (ntfail) {
	case 0:		/* Fault free. */
		if (rf_suppressLocksAndLargeWrites ||
		    (((asmap->numStripeUnitsAccessed <=
		       (layoutPtr->numDataCol / 2)) &&
		      (layoutPtr->numDataCol != 1)) ||
		     (asmap->parityInfo->next != NULL) ||
		     (asmap->qInfo->next != NULL) ||
		     rf_CheckStripeForFailures(raidPtr, asmap))) {

			*createFunc = (RF_VoidFuncPtr) rf_PQCreateSmallWriteDAG;
		} else {
			*createFunc = (RF_VoidFuncPtr) rf_PQCreateLargeWriteDAG;
		}
		break;

	case 1:		/* Single disk fault. */
		if (npfail == 1) {
			RF_ASSERT((asmap->failedPDAs[0]->type ==
			    RF_PDA_TYPE_PARITY) ||
			    (asmap->failedPDAs[0]->type == RF_PDA_TYPE_Q));
			if (asmap->failedPDAs[0]->type == RF_PDA_TYPE_Q) {
				/*
				 * Q died, treat like normal mode raid5 write.
				 */
				if (((asmap->numStripeUnitsAccessed <=
				      (layoutPtr->numDataCol / 2)) ||
				     (asmap->numStripeUnitsAccessed == 1)) ||
				    rf_NumFailedDataUnitsInStripe(raidPtr,
				     asmap))
					*createFunc = (RF_VoidFuncPtr)
					    rf_PQ_001_CreateSmallWriteDAG;
				else
					*createFunc = (RF_VoidFuncPtr)
					    rf_PQ_001_CreateLargeWriteDAG;
			} else {/* Parity died, small write only updating Q. */
				if (((asmap->numStripeUnitsAccessed <=
				      (layoutPtr->numDataCol / 2)) ||
				     (asmap->numStripeUnitsAccessed == 1)) ||
				    rf_NumFailedDataUnitsInStripe(raidPtr,
				     asmap))
					*createFunc = (RF_VoidFuncPtr)
					    rf_PQ_010_CreateSmallWriteDAG;
				else
					*createFunc = (RF_VoidFuncPtr)
					    rf_PQ_010_CreateLargeWriteDAG;
			}
		} else {	/*
				 * Data missing. Do a P reconstruct write if
				 * only a single data unit is lost in the
				 * stripe, otherwise a PQ reconstruct write.
				 */
			if (rf_NumFailedDataUnitsInStripe(raidPtr, asmap) == 2)
				*createFunc = (RF_VoidFuncPtr)
				    rf_PQ_200_CreateWriteDAG;
			else
				*createFunc = (RF_VoidFuncPtr)
				    rf_PQ_100_CreateWriteDAG;
		}
		break;

	case 2:		/* Two disk faults. */
		switch (npfail) {
		case 2:	/* Both p and q dead. */
			*createFunc = (RF_VoidFuncPtr) rf_PQ_011_CreateWriteDAG;
			break;
		case 1:	/* Either p or q and dead data. */
			RF_ASSERT(asmap->failedPDAs[0]->type ==
			          RF_PDA_TYPE_DATA);
			RF_ASSERT((asmap->failedPDAs[1]->type ==
			           RF_PDA_TYPE_PARITY) ||
			          (asmap->failedPDAs[1]->type ==
			           RF_PDA_TYPE_Q));
			if (asmap->failedPDAs[1]->type == RF_PDA_TYPE_Q)
				*createFunc = (RF_VoidFuncPtr)
				    rf_PQ_101_CreateWriteDAG;
			else
				*createFunc = (RF_VoidFuncPtr)
				    rf_PQ_110_CreateWriteDAG;
			break;
		case 0:	/* Double data loss. */
			*createFunc = (RF_VoidFuncPtr) rf_PQ_200_CreateWriteDAG;
			break;
		}
		break;

	default:	/* More than 2 disk faults. */
		*createFunc = NULL;
		RF_PANIC();
	}
	return;
}


/*
 * Used as a stop gap info function.
 */
#if 0
void
rf_PQOne(RF_Raid_t *raidPtr, int *nSucc, int *nAnte,
    RF_AccessStripeMap_t *asmap)
{
	*nSucc = *nAnte = 1;
}

void
rf_PQOneTwo(RF_Raid_t *raidPtr, int *nSucc, int *nAnte,
    RF_AccessStripeMap_t *asmap)
{
	*nSucc = 1;
	*nAnte = 2;
}
#endif

RF_CREATE_DAG_FUNC_DECL(rf_PQCreateLargeWriteDAG)
{
	rf_CommonCreateLargeWriteDAG(raidPtr, asmap, dag_h, bp, flags,
	    allocList, 2, rf_RegularPQFunc, RF_FALSE);
}

int
rf_RegularONQFunc(RF_DagNode_t *node)
{
	int np = node->numParams;
	int d;
	RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[np - 1].p;
	int i;
	RF_AccTraceEntry_t *tracerec = node->dagHdr->tracerec;
	RF_Etimer_t timer;
	char *qbuf, *qpbuf;
	char *obuf, *nbuf;
	RF_PhysDiskAddr_t *old, *new;
	unsigned long coeff;
	unsigned secPerSU = raidPtr->Layout.sectorsPerStripeUnit;

	RF_ETIMER_START(timer);

	d = (np - 3) / 4;
	RF_ASSERT(4 * d + 3 == np);
	qbuf = (char *) node->params[2 * d + 1].p;	/* Q buffer. */
	for (i = 0; i < d; i++) {
		old = (RF_PhysDiskAddr_t *) node->params[2 * i].p;
		obuf = (char *) node->params[2 * i + 1].p;
		new = (RF_PhysDiskAddr_t *) node->params[2 * (d + 1 + i)].p;
		nbuf = (char *) node->params[2 * (d + 1 + i) + 1].p;
		RF_ASSERT(new->numSector == old->numSector);
		RF_ASSERT(new->raidAddress == old->raidAddress);
		/*
		 * The stripe unit within the stripe tells us the coefficient
		 * to use for the multiply.
		 */
		coeff = rf_RaidAddressToStripeUnitID(&(raidPtr->Layout),
		    new->raidAddress);
		/*
		 * Compute the data unit offset within the column, then add
		 * one.
		 */
		coeff = (coeff % raidPtr->Layout.numDataCol);
		qpbuf = qbuf + rf_RaidAddressToByte(raidPtr,
		    old->startSector % secPerSU);
		rf_QDelta(qpbuf, obuf, nbuf, rf_RaidAddressToByte(raidPtr,
		    old->numSector), coeff);
	}

	RF_ETIMER_STOP(timer);
	RF_ETIMER_EVAL(timer);
	tracerec->q_us += RF_ETIMER_VAL_US(timer);
	rf_GenericWakeupFunc(node, 0);	/*
					 * Call wake func explicitly since no
					 * I/O in this node.
					 */
	return (0);
}


/*
 * See the SimpleXORFunc for the difference between a simple and regular func.
 * These Q functions should be used for
 *	new q = Q(data, old data, old q)
 * style updates and not for
 *	q = (new data, new data, ...)
 * computations.
 *
 * The simple q takes 2(2d+1)+1 params, where d is the number
 * of stripes written. The order of params is
 *   old data pda_0, old data buffer_0, old data pda_1, old data buffer_1, ...
 *   old data pda_d, old data buffer_d
 *   [2d] old q pda_0, old q buffer
 *   [2d_2] new data pda_0, new data buffer_0, ...
 *   new data pda_d, new data buffer_d
 *   raidPtr
 */

int
rf_SimpleONQFunc(RF_DagNode_t *node)
{
	int np = node->numParams;
	int d;
	RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[np - 1].p;
	int i;
	RF_AccTraceEntry_t *tracerec = node->dagHdr->tracerec;
	RF_Etimer_t timer;
	char *qbuf;
	char *obuf, *nbuf;
	RF_PhysDiskAddr_t *old, *new;
	unsigned long coeff;

	RF_ETIMER_START(timer);

	d = (np - 3) / 4;
	RF_ASSERT(4 * d + 3 == np);
	qbuf = (char *) node->params[2 * d + 1].p;	/* Q buffer. */
	for (i = 0; i < d; i++) {
		old = (RF_PhysDiskAddr_t *) node->params[2 * i].p;
		obuf = (char *) node->params[2 * i + 1].p;
		new = (RF_PhysDiskAddr_t *) node->params[2 * (d + 1 + i)].p;
		nbuf = (char *) node->params[2 * (d + 1 + i) + 1].p;
		RF_ASSERT(new->numSector == old->numSector);
		RF_ASSERT(new->raidAddress == old->raidAddress);
		/*
		 * The stripe unit within the stripe tells us the coefficient
		 * to use for the multiply.
		 */
		coeff = rf_RaidAddressToStripeUnitID(&(raidPtr->Layout),
		    new->raidAddress);
		/*
		 * Compute the data unit offset within the column, then add
		 * one.
		 */
		coeff = (coeff % raidPtr->Layout.numDataCol);
		rf_QDelta(qbuf, obuf, nbuf, rf_RaidAddressToByte(raidPtr,
		    old->numSector), coeff);
	}

	RF_ETIMER_STOP(timer);
	RF_ETIMER_EVAL(timer);
	tracerec->q_us += RF_ETIMER_VAL_US(timer);
	rf_GenericWakeupFunc(node, 0);	/*
					 * Call wake func explicitly since no
					 * I/O in this node.
					 */
	return (0);
}

RF_CREATE_DAG_FUNC_DECL(rf_PQCreateSmallWriteDAG)
{
	rf_CommonCreateSmallWriteDAG(raidPtr, asmap, dag_h, bp, flags,
	    allocList, &rf_pFuncs, &rf_qFuncs);
}


void rf_RegularQSubr(RF_DagNode_t *, char *);

void
rf_RegularQSubr(RF_DagNode_t *node, char *qbuf)
{
	int np = node->numParams;
	int d;
	RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[np - 1].p;
	unsigned secPerSU = raidPtr->Layout.sectorsPerStripeUnit;
	int i;
	RF_AccTraceEntry_t *tracerec = node->dagHdr->tracerec;
	RF_Etimer_t timer;
	char *obuf, *qpbuf;
	RF_PhysDiskAddr_t *old;
	unsigned long coeff;

	RF_ETIMER_START(timer);

	d = (np - 1) / 2;
	RF_ASSERT(2 * d + 1 == np);
	for (i = 0; i < d; i++) {
		old = (RF_PhysDiskAddr_t *) node->params[2 * i].p;
		obuf = (char *) node->params[2 * i + 1].p;
		coeff = rf_RaidAddressToStripeUnitID(&(raidPtr->Layout),
		    old->raidAddress);
		/*
		 * Compute the data unit offset within the column, then add
		 * one.
		 */
		coeff = (coeff % raidPtr->Layout.numDataCol);
		/*
		 * The input buffers may not all be aligned with the start of
		 * the stripe. So shift by their sector offset within the
		 * stripe unit.
		 */
		qpbuf = qbuf + rf_RaidAddressToByte(raidPtr,
		    old->startSector % secPerSU);
		rf_IncQ((unsigned long *) qpbuf, (unsigned long *) obuf,
		    rf_RaidAddressToByte(raidPtr, old->numSector), coeff);
	}

	RF_ETIMER_STOP(timer);
	RF_ETIMER_EVAL(timer);
	tracerec->q_us += RF_ETIMER_VAL_US(timer);
}


/*
 * Used in degraded writes.
 */

void rf_DegrQSubr(RF_DagNode_t *);

void
rf_DegrQSubr(RF_DagNode_t *node)
{
	int np = node->numParams;
	int d;
	RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[np - 1].p;
	unsigned secPerSU = raidPtr->Layout.sectorsPerStripeUnit;
	RF_AccTraceEntry_t *tracerec = node->dagHdr->tracerec;
	RF_Etimer_t timer;
	char *qbuf = node->results[1];
	char *obuf, *qpbuf;
	RF_PhysDiskAddr_t *old;
	unsigned long coeff;
	unsigned fail_start;
	int i, j;

	old = (RF_PhysDiskAddr_t *) node->params[np - 2].p;
	fail_start = old->startSector % secPerSU;

	RF_ETIMER_START(timer);

	d = (np - 2) / 2;
	RF_ASSERT(2 * d + 2 == np);
	for (i = 0; i < d; i++) {
		old = (RF_PhysDiskAddr_t *) node->params[2 * i].p;
		obuf = (char *) node->params[2 * i + 1].p;
		coeff = rf_RaidAddressToStripeUnitID(&(raidPtr->Layout),
		    old->raidAddress);
		/*
		 * Compute the data unit offset within the column, then add
		 * one.
		 */
		coeff = (coeff % raidPtr->Layout.numDataCol);
		/*
		 * The input buffers may not all be aligned with the start of
		 * the stripe. So shift by their sector offset within the
		 * stripe unit.
		 */
		j = old->startSector % secPerSU;
		RF_ASSERT(j >= fail_start);
		qpbuf = qbuf + rf_RaidAddressToByte(raidPtr, j - fail_start);
		rf_IncQ((unsigned long *) qpbuf, (unsigned long *) obuf,
		    rf_RaidAddressToByte(raidPtr, old->numSector), coeff);
	}

	RF_ETIMER_STOP(timer);
	RF_ETIMER_EVAL(timer);
	tracerec->q_us += RF_ETIMER_VAL_US(timer);
}


/*
 * Called by large write code to compute the new parity and the new q.
 *
 * Structure of the params:
 *
 *   pda_0, buffer_0, pda_1 , buffer_1, ..., pda_d, buffer_d (d = numDataCol)
 *   raidPtr
 *
 * For a total of 2d+1 arguments.
 * The result buffers results[0], results[1] are the buffers for the p and q,
 * respectively.
 *
 * We compute Q first, then compute P. The P calculation may try to reuse
 * one of the input buffers for its output, so if we computed P first, we would
 * corrupt the input for the q calculation.
 */

int
rf_RegularPQFunc(RF_DagNode_t *node)
{
	rf_RegularQSubr(node, node->results[1]);
	return (rf_RegularXorFunc(node));	/* Does the wakeup. */
}

int
rf_RegularQFunc(RF_DagNode_t *node)
{
	/* Almost ... adjust Qsubr args. */
	rf_RegularQSubr(node, node->results[0]);
	rf_GenericWakeupFunc(node, 0);	/*
					 * Call wake func explicitly since no
					 * I/O in this node.
					 */
	return (0);
}


/*
 * Called by singly degraded write code to compute the new parity and
 * the new q.
 *
 * Structure of the params:
 *
 *   pda_0, buffer_0, pda_1 , buffer_1, ..., pda_d, buffer_d
 *   failedPDA raidPtr
 *
 * for a total of 2d+2 arguments.
 * The result buffers results[0], results[1] are the buffers for the parity
 * and q, respectively.
 *
 * We compute Q first, then compute parity. The parity calculation may try
 * to reuse one of the input buffers for its output, so if we computed parity
 * first, we would corrupt the input for the q calculation.
 *
 * We treat this identically to the regularPQ case, ignoring the failedPDA
 * extra argument.
 */

void
rf_Degraded_100_PQFunc(RF_DagNode_t *node)
{
	int     np = node->numParams;

	RF_ASSERT(np >= 2);
	rf_DegrQSubr(node);
	rf_RecoveryXorFunc(node);
}


/*
 * The two below are used when reading a stripe with a single lost data unit.
 * The parameters are
 *
 *  pda_0, buffer_0, ..., pda_n, buffer_n, P pda, P buffer, failedPDA, raidPtr
 *
 * and results[0] contains the data buffer, which is originally zero-filled.
 */

/*
 * This Q func is used by the degraded-mode dag functions to recover lost data.
 * The second-to-last parameter is the PDA for the failed portion of the
 * access. The code here looks at this PDA and assumes that the xor target
 * buffer is equal in size to the number of sectors in the failed PDA. It then
 * uses the other PDAs in the parameter list to determine where within the
 * target buffer the corresponding data should be xored.
 *
 * Recall the basic equation is
 *
 *     Q = (data_1 + 2 * data_2 ... + k * data_k) mod 256
 *
 * so to recover data_j we need
 *
 *    J data_j = (Q - data_1 - 2 data_2 ... - k * data_k) mod 256
 *
 * So the coefficient for each buffer is (255 - data_col), and j should be
 * initialized by copying Q into it. Then we need to do a table lookup to
 * convert to solve
 *   data_j /= J
 *
 */

int
rf_RecoveryQFunc(RF_DagNode_t *node)
{
	RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[node->numParams - 1].p;
	RF_RaidLayout_t *layoutPtr = (RF_RaidLayout_t *) & raidPtr->Layout;
	RF_PhysDiskAddr_t *failedPDA =
	    (RF_PhysDiskAddr_t *) node->params[node->numParams - 2].p;
	int i;
	RF_PhysDiskAddr_t *pda;
	RF_RaidAddr_t suoffset;
	RF_RaidAddr_t failedSUOffset =
	    rf_StripeUnitOffset(layoutPtr, failedPDA->startSector);
	char *srcbuf, *destbuf;
	RF_AccTraceEntry_t *tracerec = node->dagHdr->tracerec;
	RF_Etimer_t timer;
	unsigned long coeff;

	RF_ETIMER_START(timer);
	/* Start by copying Q into the buffer. */
	bcopy(node->params[node->numParams - 3].p, node->results[0],
	    rf_RaidAddressToByte(raidPtr, failedPDA->numSector));
	for (i = 0; i < node->numParams - 4; i += 2) {
		RF_ASSERT(node->params[i + 1].p != node->results[0]);
		pda = (RF_PhysDiskAddr_t *) node->params[i].p;
		srcbuf = (char *) node->params[i + 1].p;
		suoffset = rf_StripeUnitOffset(layoutPtr, pda->startSector);
		destbuf = ((char *) node->results[0]) +
		    rf_RaidAddressToByte(raidPtr, suoffset - failedSUOffset);
		coeff = rf_RaidAddressToStripeUnitID(&(raidPtr->Layout),
		    pda->raidAddress);
		/* Compute the data unit offset within the column. */
		coeff = (coeff % raidPtr->Layout.numDataCol);
		rf_IncQ((unsigned long *) destbuf, (unsigned long *) srcbuf,
		    rf_RaidAddressToByte(raidPtr, pda->numSector), coeff);
	}
	/* Do the nasty inversion now. */
	coeff = (rf_RaidAddressToStripeUnitID(&(raidPtr->Layout),
	    failedPDA->startSector) % raidPtr->Layout.numDataCol);
	rf_InvertQ(node->results[0], node->results[0],
	    rf_RaidAddressToByte(raidPtr, pda->numSector), coeff);
	RF_ETIMER_STOP(timer);
	RF_ETIMER_EVAL(timer);
	tracerec->q_us += RF_ETIMER_VAL_US(timer);
	rf_GenericWakeupFunc(node, 0);
	return (0);
}

int
rf_RecoveryPQFunc(RF_DagNode_t *node)
{
	RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[node->numParams - 1].p;
	printf("raid%d: Recovery from PQ not implemented.\n", raidPtr->raidid);
	return (1);
}


/*
 * Degraded write Q subroutine.
 * Used when P is dead.
 * Large-write style Q computation.
 * Parameters:
 *
 * (pda, buf), (pda, buf), ..., (failedPDA, bufPtr), failedPDA, raidPtr.
 *
 * We ignore failedPDA.
 *
 * This is a "simple style" recovery func.
 */

void
rf_PQ_DegradedWriteQFunc(RF_DagNode_t *node)
{
	int np = node->numParams;
	int d;
	RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[np - 1].p;
	unsigned secPerSU = raidPtr->Layout.sectorsPerStripeUnit;
	RF_AccTraceEntry_t *tracerec = node->dagHdr->tracerec;
	RF_Etimer_t timer;
	char *qbuf = node->results[0];
	char *obuf, *qpbuf;
	RF_PhysDiskAddr_t *old;
	unsigned long coeff;
	int fail_start, i, j;

	old = (RF_PhysDiskAddr_t *) node->params[np - 2].p;
	fail_start = old->startSector % secPerSU;

	RF_ETIMER_START(timer);

	d = (np - 2) / 2;
	RF_ASSERT(2 * d + 2 == np);

	for (i = 0; i < d; i++) {
		old = (RF_PhysDiskAddr_t *) node->params[2 * i].p;
		obuf = (char *) node->params[2 * i + 1].p;
		coeff = rf_RaidAddressToStripeUnitID(&(raidPtr->Layout),
		    old->raidAddress);
		/*
		 * Compute the data unit offset within the column, then add
		 * one.
		 */
		coeff = (coeff % raidPtr->Layout.numDataCol);
		j = old->startSector % secPerSU;
		RF_ASSERT(j >= fail_start);
		qpbuf = qbuf + rf_RaidAddressToByte(raidPtr, j - fail_start);
		rf_IncQ((unsigned long *) qpbuf, (unsigned long *) obuf,
		    rf_RaidAddressToByte(raidPtr, old->numSector), coeff);
	}

	RF_ETIMER_STOP(timer);
	RF_ETIMER_EVAL(timer);
	tracerec->q_us += RF_ETIMER_VAL_US(timer);
	rf_GenericWakeupFunc(node, 0);
}


/* Q computations. */

/*
 * Coeff - colummn;
 *
 * Compute  dest ^= qfor[28-coeff][rn[coeff+1] a]
 *
 * On 5-bit basis;
 * Length in bytes;
 */

void
rf_IncQ(unsigned long *dest, unsigned long *buf, unsigned length,
    unsigned coeff)
{
	unsigned long a, d, new;
	unsigned long a1, a2;
	unsigned int *q = &(rf_qfor[28 - coeff][0]);
	unsigned r = rf_rn[coeff + 1];

#define	EXTRACT(a,i)	((a >> (5L*i)) & 0x1f)
#define	INSERT(a,i)	(a << (5L*i))

	length /= 8;
	/* 13 5 bit quants in a 64 bit word. */
	while (length) {
		a = *buf++;
		d = *dest;
		a1 = EXTRACT(a, 0) ^ r;
		a2 = EXTRACT(a, 1) ^ r;
		new = INSERT(a2, 1) | a1;
		a1 = EXTRACT(a, 2) ^ r;
		a2 = EXTRACT(a, 3) ^ r;
		a1 = q[a1];
		a2 = q[a2];
		new = new | INSERT(a1, 2) | INSERT(a2, 3);
		a1 = EXTRACT(a, 4) ^ r;
		a2 = EXTRACT(a, 5) ^ r;
		a1 = q[a1];
		a2 = q[a2];
		new = new | INSERT(a1, 4) | INSERT(a2, 5);
		a1 = EXTRACT(a, 5) ^ r;
		a2 = EXTRACT(a, 6) ^ r;
		a1 = q[a1];
		a2 = q[a2];
		new = new | INSERT(a1, 5) | INSERT(a2, 6);
#if	RF_LONGSHIFT > 2
		a1 = EXTRACT(a, 7) ^ r;
		a2 = EXTRACT(a, 8) ^ r;
		a1 = q[a1];
		a2 = q[a2];
		new = new | INSERT(a1, 7) | INSERT(a2, 8);
		a1 = EXTRACT(a, 9) ^ r;
		a2 = EXTRACT(a, 10) ^ r;
		a1 = q[a1];
		a2 = q[a2];
		new = new | INSERT(a1, 9) | INSERT(a2, 10);
		a1 = EXTRACT(a, 11) ^ r;
		a2 = EXTRACT(a, 12) ^ r;
		a1 = q[a1];
		a2 = q[a2];
		new = new | INSERT(a1, 11) | INSERT(a2, 12);
#endif	/* RF_LONGSHIFT > 2 */
		d ^= new;
		*dest++ = d;
		length--;
	}
}


/*
 * Compute.
 *
 * dest ^= rf_qfor[28-coeff][rf_rn[coeff+1] (old^new)]
 *
 * On a five bit basis.
 * Optimization: compute old ^ new on 64 bit basis.
 *
 * Length in bytes.
 */

void
rf_QDelta(char *dest, char *obuf, char *nbuf, unsigned length,
    unsigned char coeff)
{
	unsigned long a, d, new;
	unsigned long a1, a2;
	unsigned int *q = &(rf_qfor[28 - coeff][0]);
	unsigned int r = rf_rn[coeff + 1];

	r = a1 = a2 = new = d = a = 0; /* XXX For now... */
	q = NULL; /* XXX For now */

#ifdef	_KERNEL
	/*
	 * PQ in kernel currently not supported because the encoding/decoding
	 * table is not present.
	 */
	bzero(dest, length);
#else	/* _KERNEL */
	/* This code probably doesn't work and should be rewritten. -wvcii */
	/* 13 5 bit quants in a 64 bit word. */
	length /= 8;
	while (length) {
		a = *obuf++;	/*
				 * XXX Need to reorg to avoid cache conflicts.
				 */
		a ^= *nbuf++;
		d = *dest;
		a1 = EXTRACT(a, 0) ^ r;
		a2 = EXTRACT(a, 1) ^ r;
		a1 = q[a1];
		a2 = q[a2];
		new = INSERT(a2, 1) | a1;
		a1 = EXTRACT(a, 2) ^ r;
		a2 = EXTRACT(a, 3) ^ r;
		a1 = q[a1];
		a2 = q[a2];
		new = new | INSERT(a1, 2) | INSERT(a2, 3);
		a1 = EXTRACT(a, 4) ^ r;
		a2 = EXTRACT(a, 5) ^ r;
		a1 = q[a1];
		a2 = q[a2];
		new = new | INSERT(a1, 4) | INSERT(a2, 5);
		a1 = EXTRACT(a, 5) ^ r;
		a2 = EXTRACT(a, 6) ^ r;
		a1 = q[a1];
		a2 = q[a2];
		new = new | INSERT(a1, 5) | INSERT(a2, 6);
#if	RF_LONGSHIFT > 2
		a1 = EXTRACT(a, 7) ^ r;
		a2 = EXTRACT(a, 8) ^ r;
		a1 = q[a1];
		a2 = q[a2];
		new = new | INSERT(a1, 7) | INSERT(a2, 8);
		a1 = EXTRACT(a, 9) ^ r;
		a2 = EXTRACT(a, 10) ^ r;
		a1 = q[a1];
		a2 = q[a2];
		new = new | INSERT(a1, 9) | INSERT(a2, 10);
		a1 = EXTRACT(a, 11) ^ r;
		a2 = EXTRACT(a, 12) ^ r;
		a1 = q[a1];
		a2 = q[a2];
		new = new | INSERT(a1, 11) | INSERT(a2, 12);
#endif	/* RF_LONGSHIFT > 2 */
		d ^= new;
		*dest++ = d;
		length--;
	}
#endif	/* _KERNEL */
}


/*
 * Recover columns a and b from the given p and q into
 * bufs abuf and bbuf. All bufs are word aligned.
 * Length is in bytes.
 */

/*
 * XXX
 *
 * Everything about this seems wrong.
 */

void
rf_PQ_recover(unsigned long *pbuf, unsigned long *qbuf, unsigned long *abuf,
    unsigned long *bbuf, unsigned length, unsigned coeff_a, unsigned coeff_b)
{
	unsigned long p, q, a, a0, a1;
	int col = (29 * coeff_a) + coeff_b;
	unsigned char *q0 = &(rf_qinv[col][0]);

	length /= 8;
	while (length) {
		p = *pbuf++;
		q = *qbuf++;
		a0 = EXTRACT(p, 0);
		a1 = EXTRACT(q, 0);
		a = q0[a0 << 5 | a1];

#define	MF(i)								\
do {									\
	a0 = EXTRACT(p, i);						\
	a1 = EXTRACT(q, i);						\
	a  = a | INSERT(q0[a0<<5 | a1], i);				\
} while (0)

		MF(1);
		MF(2);
		MF(3);
		MF(4);
		MF(5);
		MF(6);
#if 0
		MF(7);
		MF(8);
		MF(9);
		MF(10);
		MF(11);
		MF(12);
#endif	/* 0 */
		*abuf++ = a;
		*bbuf++ = a ^ p;
		length--;
	}
}


/*
 * Lost parity and a data column. Recover that data column.
 * Assume col coeff is lost. Let q the contents of Q after
 * all surviving data columns have been q-xored out of it.
 * Then we have the equation
 *
 *   q[28-coeff][a_i ^ r_i+1] = q
 *
 * but q is cyclic with period 31.
 * So q[3+coeff][q[28-coeff][a_i ^ r_{i+1}]] =
 *    q[31][a_i ^ r_{i+1}] = a_i ^ r_{i+1} .
 *
 * so a_i = r_{coeff+1} ^ q[3+coeff][q]
 *
 * The routine is passed q buffer and the buffer
 * the data is to be recoverd into. They can be the same.
 */

void
rf_InvertQ(unsigned long *qbuf, unsigned long *abuf, unsigned length,
    unsigned coeff)
{
	unsigned long a, new;
	unsigned long a1, a2;
	unsigned int *q = &(rf_qfor[3 + coeff][0]);
	unsigned r = rf_rn[coeff + 1];

	/* 13 5 bit quants in a 64 bit word. */
	length /= 8;
	while (length) {
		a = *qbuf++;
		a1 = EXTRACT(a, 0);
		a2 = EXTRACT(a, 1);
		a1 = r ^ q[a1];
		a2 = r ^ q[a2];
		new = INSERT(a2, 1) | a1;

#define	M(i,j)								\
do {									\
	a1 = EXTRACT(a, i);						\
	a2 = EXTRACT(a, j);						\
	a1 = r ^ q[a1];							\
	a2 = r ^ q[a2];							\
	new = new | INSERT(a1, i) | INSERT(a2, j);			\
} while (0)

		M(2, 3);
		M(4, 5);
		M(5, 6);
#if	RF_LONGSHIFT > 2
		M(7, 8);
		M(9, 10);
		M(11, 12);
#endif	/* RF_LONGSHIFT > 2 */
		*abuf++ = new;
		length--;
	}
}
#endif	/* (RF_INCLUDE_DECL_PQ > 0) || (RF_INCLUDE_RAID6 > 0) */
